//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//           Source: Simple LingoSim Program
//          Compile: g++ LingoSim.cpp -o LingoSim
//Execution Example:
//  1. For SMILES -> ./LingoSim InputFile OutputFile 0 0
//  2. For  IUPAC -> ./LingoSim InputFile OutputFile 1 0
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <vector>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LINGOLENGTH  4
#define LENGTHARRAY  10000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct SmilesRef {

  char  *smiles;
  char  *ref;

  SmilesRef() {
    this->smiles = NULL;
    this->ref    = NULL;
  }

};

#define SMILESRef struct SmilesRef
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double obtainLingoSimSMILES(char *lingoString1, char *lingoString2, int type) {

  if (strlen(lingoString1) < LINGOLENGTH || strlen(lingoString2) < LINGOLENGTH) return 0;

  int length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  char tmpLingo1[length1], tmpLingo2[length2];
  int  i, j, k, z;

  //Change Cl by L and Br by R in the two input Smiles names
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    if (i < length1) {
      if      ((lingoString1[i] == 'l') || (lingoString1[i] == 'r')) tmpLingo1[j - 1] = (lingoString1[i] == 'l')?'L':'R';
      else if (isdigit(lingoString1[i]) && type) tmpLingo1[j++] = '0';
      else tmpLingo1[j++] = lingoString1[i];
    }
    if (i < length2) {
      if ((lingoString2[i] == 'l') || (lingoString2[i] == 'r')) tmpLingo2[k - 1] = (lingoString2[i] == 'l')?'L':'R';
      else if (isdigit(lingoString2[i]) && type) tmpLingo2[k++] = '0';
      else tmpLingo2[k++] = lingoString2[i];
    }
  }
  tmpLingo1[j] = '\0'; tmpLingo2[k] = '\0';

  j -= (LINGOLENGTH - 1); k -= (LINGOLENGTH - 1);
  int    L = j + k; 
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {
      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {
      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1.0 - ((double)(Nai - Nbi)/(double)(Nai + Nbi))):(1 - ((double)(Nbi - Nai)/(double)(Nai + Nbi))));
    else                 L--;
  }

  return (double)((double)similarityValue/(double)L);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double obtainLingoSim(char *lingoString1, char *lingoString2, int type) {

  if (strlen(lingoString1) < LINGOLENGTH || strlen(lingoString2) < LINGOLENGTH) return 0;

  int length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  char tmpLingo1[length1], tmpLingo2[length2];
  int  i, j, k, z;

  //Change Cl by L and Br by R in the two input Smiles names
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    if (i < length1) {
      if (isdigit(lingoString1[i]) && type) tmpLingo1[j++] = '0';
      else tmpLingo1[j++] = lingoString1[i];
    }
    if (i < length2) {
      if (isdigit(lingoString2[i]) && type) tmpLingo2[k++] = '0';
      else tmpLingo2[k++] = lingoString2[i];
    }
  }
  tmpLingo1[j] = '\0'; tmpLingo2[k] = '\0';

  j -= (LINGOLENGTH - 1); k -= (LINGOLENGTH - 1);
  int    L = j + k;
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {
      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {
      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1.0 - ((double)(Nai - Nbi)/(double)(Nai + Nbi))):(1 - ((double)(Nbi - Nai)/(double)(Nai + Nbi))));
    else                 L--;
  }

  return (double)((double)similarityValue/(double)L);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void trim(char *text) {

  int i, j;
  for (i = 0, j = 0; i < strlen(text); i++)
    if ((text[i] != '\n') && (text[i] != '\r'))
      text[j++] = text[i];
  text[j] = '\0';

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* splitPosLine(char *line, int pos, char character) {

  if (pos < 0 || !line) return NULL;

  int   count = 1, length = 0;
  char *result = new char[strlen(line) + 1];

  for (int i = 0; i < strlen(line); i++) {
    if (line[i] == character) { count++; i++; }
    if (pos == count) result[length++] = line[i];
  }
  result[length] = '\0';

  return result;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main(int argc, char *argv[]) {

  if (argc < 3) {
    printf("Error in the input parameters! Must by like: \n");
    printf("./LingoSim <Input File> <Output File> [Kind of Input (0 -> SMILES (Default), 1 -> OTHERS NAMES)] [Type (0 -> Normal (Default), 1 -> All numbers are equal)]\n");
    return 1;
  }

  char               *nameSeqFile      = new char[strlen(argv[1]) + 1]; strcpy(nameSeqFile, argv[1]);
  char               *nameOutFile      = new char[strlen(argv[2]) + 1]; strcpy(nameOutFile, argv[2]);
  char                buffer[LENGTHARRAY];

  int                 input            = (argc > 3)?((!atoi(argv[3])?0:1)):0;
  int                 type             = (argc > 4)?((!atoi(argv[4])?0:1)):0;

  vector <SMILESRef>  listSequencesRef;
  
  FILE               *sequenceFile     = fopen(nameSeqFile,   "r");

  while (!feof(sequenceFile)) {

    fgets(buffer, LENGTHARRAY, sequenceFile); trim(buffer);
    if (!buffer || !strlen(buffer)) continue;

    SMILESRef tmpSmilesRef;
    tmpSmilesRef.smiles = splitPosLine(buffer, 1, ' '); trim(tmpSmilesRef.smiles);
    tmpSmilesRef.ref    = splitPosLine(buffer, 2, ' '); trim(tmpSmilesRef.ref);

    listSequencesRef.push_back(tmpSmilesRef);
    
  }

  fclose(sequenceFile);

  vector<SMILESRef>::iterator  it, it_;
  FILE                        *outFile = fopen(nameOutFile,   "w+");

  for (it = listSequencesRef.begin(); it < listSequencesRef.end(); it++)
    fprintf(outFile, "%8s ", (*it).ref);
  fprintf(outFile, "\n");

  for (it = listSequencesRef.begin(); it < listSequencesRef.end(); it++) {
    for (it_ = listSequencesRef.begin(); it_ < listSequencesRef.end(); it_++) {
      if (!input) fprintf(outFile, "%08f ", obtainLingoSimSMILES((*it).smiles, (*it_).smiles, type));
      else        fprintf(outFile, "%08f ", obtainLingoSim((*it).smiles, (*it_).smiles, type));
    }
    fprintf(outFile, "\n");
  }

  fclose(outFile);

  if (nameSeqFile)      delete[] nameSeqFile;
  if (nameOutFile)      delete[] nameOutFile;

  return 0;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//

